bitkeeper revision 1.1159.183.4 (41a26aceaXU8CHj-DqjbnM3q7zLf2g)
authoriap10@labyrinth.cl.cam.ac.uk <iap10@labyrinth.cl.cam.ac.uk>
Mon, 22 Nov 2004 22:40:14 +0000 (22:40 +0000)
committeriap10@labyrinth.cl.cam.ac.uk <iap10@labyrinth.cl.cam.ac.uk>
Mon, 22 Nov 2004 22:40:14 +0000 (22:40 +0000)
Merge ssh://xenbk@gandalf.hpl.hp.com//var/bk/xeno-unstable.bk
into labyrinth.cl.cam.ac.uk:/auto/anfs/scratch/labyrinth/iap10/xeno-clone/xeno.bk

1  2 
tools/libxutil/sxpr_parser.c

index 96b4d0851609bd6329b561dd4f07411bc7f273f9,0000000000000000000000000000000000000000..f7a19a45f744a34d5c22e0492aeab0cb89dd1eda
mode 100644,000000..100644
--- /dev/null
@@@ -1,959 -1,0 +1,933 @@@
-     } else if(CONSP(p->start_state->val)){
 +/*
 + * Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
 + *
 + * This library is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU Lesser General Public License as
 + * published by the Free Software Foundation; either version 2.1 of the
 + * License, or  (at your option) any later version. This library is 
 + * distributed in the  hope that it will be useful, but WITHOUT ANY
 + * WARRANTY; without even the implied warranty of MERCHANTABILITY or
 + * FITNESS FOR A PARTICULAR PURPOSE.
 + * See the GNU Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public License
 + * along with this library; if not, write to the Free Software Foundation,
 + * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 + */
 +
 +#ifdef __KERNEL__
 +#  include <linux/config.h>
 +#  include <linux/module.h>
 +#  include <linux/kernel.h>
 +#  include <linux/string.h>
 +#  include <linux/errno.h>
 +#else
 +#  include <stdlib.h>
 +#  include <errno.h>
 +#endif
 +
 +#include "iostream.h"
 +#include "lexis.h"
 +#include "sxpr_parser.h"
 +#include "sys_string.h"
 +#include "enum.h"
 +
 +/** @file
 + * Sxpr parsing.
 + *
 + * So that the parser does not leak memory, all sxprs constructed by
 + * the parser must be freed on error.  On successful parse the sxpr
 + * returned becomes the responsibility of the caller.
 + *
 + * @author Mike Wray <mike.wray@hpl.hp.com>
 + */
 +
 +#define dprintf(fmt, args...) IOStream_print(iostdout, "[DEBUG] %s" fmt, __FUNCTION__, ##args)
 +#define printf(fmt, args...)   IOStream_print(iostdout, fmt, ##args)
 +
 +static void reset(Parser *z);
 +static int inputchar(Parser *p, char c);
 +static int savechar(Parser *p, char c);
 +extern void parse_error(Parser *in);
 +extern void parse_error_id(Parser *in, ParseErrorId id);
 +
 +static int begin_start(Parser *p, char c);
 +static int state_start(Parser *p, char c);
 +static int end_start(Parser *p);
 +
 +static int begin_comment(Parser *p, char c);
 +static int state_comment(Parser *p, char c);
 +static int end_comment(Parser *p);
 +
 +static int begin_string(Parser *p, char c);
 +static int state_string(Parser *p, char c);
 +static int end_string(Parser *p);
 +static int state_escape(Parser *p, char c);
 +static int state_octal(Parser *p, char c);
 +static int state_hex(Parser *p, char c);
 +
 +static int begin_atom(Parser *p, char c);
 +static int state_atom(Parser *p, char c);
 +static int end_atom(Parser *p);
 +
 +static int state_list(Parser *p, char c);
 +static int begin_list(Parser *p, char c);
 +static int end_list(Parser *p);
 +
 +/** Print a parse error.
 + *
 + * @param in parser
 + * @param msg format followed by printf arguments
 + */
 +void eprintf(Parser *in, char *msg, ...){
 +    va_list args;
 +    if(in->error_out){
 +        va_start(args, msg);
 +        IOStream_vprint(in->error_out, msg, args);
 +        va_end(args);
 +    }
 +}
 +
 +/** Print a parse warning.
 + *
 + * @param in parser
 + * @param msg format followed by printf arguments
 + */
 +void wprintf(Parser *in, char *msg, ...){
 +    va_list args;
 +    if(in->error_out){
 +        va_start(args, msg);
 +        IOStream_vprint(in->error_out, msg, args);
 +        va_end(args);
 +    }
 +}
 +
 +/*============================================================================*/
 +
 +/** Record defining the message for a parse error. */
 +typedef struct {
 +  ParseErrorId id;
 +  char *message;
 +} ParseError;
 +
 +/** Format for printing parse error messages. */
 +#define PARSE_ERR_FMT "parse error> line %3d, column %2d: %s"
 +
 +/** Message catalog for the parse error codes. */
 +static ParseError catalog[] = {
 +  { PARSE_ERR_UNSPECIFIED,            "unspecified error" },
 +  { PARSE_ERR_NOMEM,                  "out of memory" },
 +  { PARSE_ERR_UNEXPECTED_EOF,         "unexpected end of input" },
 +  { PARSE_ERR_TOKEN_TOO_LONG,         "token too long" },
 +  { PARSE_ERR_INVALID_SYNTAX,         "syntax error" },
 +  { PARSE_ERR_INVALID_ESCAPE,         "invalid escape" },
 +  { 0, NULL }
 +};
 +
 +/** Number of entries in the message catalog. */
 +const static int catalog_n = sizeof(catalog)/sizeof(ParseError);
 +
 +void ParserState_free(ParserState *z){
 +    if(!z) return;
 +    objfree(z->val);
 +    deallocate(z);
 +}
 +
 +int ParserState_new(ParserStateFn *fn, char *name,
 +                    ParserState *parent, ParserState **val){
 +    int err = 0;
 +    ParserState *z;
 +    z = ALLOCATE(ParserState);
 +    if(z){
 +        z->name = name;
 +        z->fn = fn;
 +        z->parent = parent;
 +        z->val = ONULL;
 +    } else {
 +        err = -ENOMEM;
 +    }
 +    if(!err) *val = z;
 +    return err;
 +}
 +
 +/** Free a parser.
 + * No-op if the parser is null.
 + *
 + * @param z parser 
 + */
 +void Parser_free(Parser *z){
 +    if(!z) return;
 +    objfree(z->val);
 +    z->val = ONONE;
 +    deallocate(z);
 +}
 +
 +/** Create a new parser. The error stream defaults to null.
 + */
 +Parser * Parser_new(void){
 +    Parser *z = ALLOCATE(Parser);
 +    int err = -ENOMEM;
 +  
 +    if(!z) goto exit;
 +    err = 0;
 +    reset(z);
 +  exit:
 +    if(err){
 +        Parser_free(z);
 +        z = NULL;
 +    }
 +    return z;
 +}
 +
 +/** Get the next character.
 + * Records the character read in the parser,
 + * and sets the line and character counts.
 + *
 + * @param p parser
 + * @return error flag: 0 on success, non-zero on error
 + */
 +static int inputchar(Parser *p, char c){
 +    int err = 0;
 +    if(c=='\n'){
 +        p->line_no++;
 +        p->char_no = 0;
 +    } else {
 +        p->char_no++;
 +    }
 +    return err;
 +}
 +
 +static int savechar(Parser *p, char c){
 +    int err = 0;
 +    if(p->buf_i >= p->buf_n){
 +        err = -ENOMEM;
 +        goto exit;
 +    }
 +    p->buf[p->buf_i] = c;
 +    p->buf_i++;
 +  exit:
 +    return err;
 +}
 +
 +int Parser_input_char(Parser *p, char c){
 +    int err = 0;
 +    if(at_eof(p)){
 +        //skip;
 +    } else {
 +        inputchar(p, c);
 +    }
 +    if(!p->state){
 +        err = begin_start(p, c);
 +        if(err) goto exit;
 +    }
 +    err = p->state->fn(p, c);
 +  exit:
 +    return err;
 +}
 +
 +int Parser_input_eof(Parser *p){
 +    int err = 0;
 +    p->eof = 1;
 +    err = Parser_input_char(p, IOSTREAM_EOF);
 +    return err;
 +}
 +
 +int Parser_input(Parser *p, char *buf, int buf_n){
 +    int err = 0;
 +    int i = 0;
 +    if(buf_n <= 0){
 +        err = Parser_input_eof(p);
 +        goto exit;
 +    }
 +    for(i = 0; i<buf_n; i++){
 +        err = Parser_input_char(p, buf[i]);
 +        if(err) goto exit;
 +    }
 +  exit:
 +    err = (err < 0 ? err : buf_n);
 +    return err;
 +}
 +
 +int Parser_push(Parser *p, ParserStateFn *fn, char *name){
 +    int err = 0;
 +    err = ParserState_new(fn, name, p->state, &p->state);
 +    return err;
 +}
 +        
 +int Parser_pop(Parser *p){
 +    int err = 0;
 +    ParserState *s = p->state;
 +    p->state = s->parent;
++    if (p->start_state == s)
++        p->start_state = NULL;
 +    ParserState_free(s);
 +    return err;
 +}
 +
 +int Parser_return(Parser *p){
 +    int err = 0;
 +    Sxpr val = ONONE;
 +    if(!p->state){
 +        err = -EINVAL;
 +        goto exit;
 +    }
 +    val = p->state->val;
 +    p->state->val = ONONE;
 +    err = Parser_pop(p);
 +    if(err) goto exit;
 +    if(p->state){
 +        err = cons_push(&p->state->val, val);
 +    } else {
 +        val = nrev(val);
 +        p->val = val;
 +    }
 +  exit:
 +    if(err){
 +        objfree(val);
 +    }
 +    return err;
 +}
 +
 +/** Determine if a character is a separator.
 + *
 + * @param p parser
 + * @param c character to test
 + * @return 1 if a separator, 0 otherwise
 + */
 +static int is_separator(Parser *p, char c){
 +    return in_sep_class(c);
 +}
 +
 +/** Return the current token.
 + * The return value points at the internal buffer, so
 + * it must not be modified (or freed). Use copy_token() if you need a copy.
 + *
 + * @param p parser
 + * @return token
 + */
 +char *peek_token(Parser *p){
 +    return p->buf;
 +}
 +
 +/** Return a copy of the current token.
 + * The returned value should be freed when finished with.
 + *
 + * @param p parser
 + * @return copy of token
 + */
 +char *copy_token(Parser *p){
 +    return strdup(peek_token(p));
 +}
 +
 +static int do_intern(Parser *p){
 +    int err = 0;
 +    Sxpr obj = intern(peek_token(p));
 +    if(NOMEMP(obj)){
 +        err = -ENOMEM;
 +    } else {
 +        p->state->val = obj;
 +    }
 +    return err;
 +}
 +
 +static int do_string(Parser *p){
 +    int err = 0;
 +    Sxpr obj;
 +    obj = string_new(peek_token(p));
 +    if(NOMEMP(obj)){
 +        err = -ENOMEM;
 +    } else {
 +        p->state->val = obj;
 +    }
 +    return err;
 +}
 +
 +void newtoken(Parser *p){
 +    memset(p->buf, 0, p->buf_n);
 +    p->buf_i = 0;
 +    p->tok_begin_line = p->line_no;
 +    p->tok_begin_char = p->char_no;
 +}
 +
 +int get_escape(char c, char *d){
 +    int err = 0;
 +    switch(c){
 +    case 'a':            *d = '\a'; break;
 +    case 'b':            *d = '\b'; break;
 +    case 'f':            *d = '\f'; break;
 +    case 'n':            *d = '\n'; break;
 +    case 'r':            *d = '\r'; break;
 +    case 't':            *d = '\t'; break;
 +    case 'v':            *d = '\v'; break;
 +    case c_escape:       *d = c_escape; break;
 +    case c_single_quote: *d = c_single_quote; break;
 +    case c_double_quote: *d = c_double_quote; break;
 +    default:
 +        err = -EINVAL;
 +    }
 +    return err;
 +}
 +
 +int Parser_ready(Parser *p){
 +    return CONSP(p->val) || (p->start_state && CONSP(p->start_state->val));
 +}
 +
 +Sxpr Parser_get_val(Parser *p){
 +    Sxpr v = ONONE;
 +    if(CONSP(p->val)){
 +        v = CAR(p->val);
 +        p->val = CDR(p->val);
 +    } else if (CONSP(p->start_state->val)){
 +        p->val = p->start_state->val;
 +        p->val = nrev(p->val);
 +        p->start_state->val = ONULL;
 +        v = CAR(p->val);
 +        p->val = CDR(p->val);
 +    }        
 +    return v;
 +}
 +
 +Sxpr Parser_get_all(Parser *p){
 +    Sxpr v = ONULL;
 +    if(CONSP(p->val)){
 +        v = p->val;
 +        p->val = ONONE;
- //#define SXPR_PARSER_MAIN
++    } else if(p->start_state && CONSP(p->start_state->val)){
 +        v = p->start_state->val;
 +        p->start_state->val = ONULL;
 +        v = nrev(v);
 +    }
 +    return v;
 +}
 +    
 +int begin_start(Parser *p, char c){
 +    int err = 0;
 +    err = Parser_push(p, state_start, "start");
 +    if(err) goto exit;
 +    p->start_state = p->state;
 +  exit:
 +    return err;
 +}
 +
 +int state_start(Parser *p, char c){
 +    int err = 0;
 +    if(at_eof(p)){
 +        err = end_start(p);
 +    } else if(in_space_class(c)){
 +        //skip
 +    } else if(in_comment_class(c)){
 +        begin_comment(p, c);
 +    } else if(c == c_list_open){
 +        begin_list(p, c);
 +    } else if(c == c_list_close){
 +        parse_error(p);
 +        err = -EINVAL;
 +    } else if(in_string_quote_class(c)){
 +        begin_string(p, c);
 +    } else if(in_printable_class(c)){
 +        begin_atom(p, c);
 +    } else if(c == 0x04){
 +        //ctrl-D, EOT: end-of-text.
 +        Parser_input_eof(p);
 +    } else {
 +        parse_error(p);
 +        err = -EINVAL;
 +    }
 +    return err;
 +}
 +
 +int end_start(Parser *p){
 +    int err = 0;
 +    err = Parser_return(p);
 +    return err;
 +}
 +
 +int begin_comment(Parser *p, char c){
 +    int err = 0;
 +    err = Parser_push(p, state_comment, "comment");
 +    if(err) goto exit;
 +    err = inputchar(p, c);
 +  exit:
 +    return err;
 +}
 +
 +int state_comment(Parser *p, char c){
 +    int err = 0;
 +    if(c == '\n' || at_eof(p)){
 +        err = end_comment(p);
 +    } else {
 +        err = inputchar(p, c);
 +    }
 +    return err;
 +}
 +
 +int end_comment(Parser *p){
 +    return Parser_pop(p);
 +}
 +
 +int begin_string(Parser *p, char c){
 +    int err = 0;
 +    err = Parser_push(p, state_string, "string");
 +    if(err) goto exit;
 +    newtoken(p);
 +    p->state->delim = c;
 +  exit:
 +    return err;
 +}
 +
 +int state_string(Parser *p, char c){
 +    int err = 0;
 +    if(at_eof(p)){
 +        parse_error_id(p, PARSE_ERR_UNEXPECTED_EOF);
 +        err = -EINVAL;
 +    } else if(c == p->state->delim){
 +        err = end_string(p);
 +    } else if(c == '\\'){
 +        err = Parser_push(p, state_escape, "escape");
 +    } else {
 +        err = savechar(p, c);
 +    }
 +    return err;
 +}
 +
 +int end_string(Parser *p){
 +    int err = 0;
 +    err = do_string(p);
 +    if(err) goto exit;
 +    err = Parser_return(p);
 +  exit:
 +    return err;
 +}
 +
 +int state_escape(Parser *p, char c){
 +    int err = 0;
 +    char d;
 +    if(at_eof(p)){
 +        parse_error_id(p, PARSE_ERR_UNEXPECTED_EOF);
 +        err = -EINVAL;
 +        goto exit;
 +    }
 +    if(get_escape(c, &d) == 0){
 +        err = savechar(p, d);
 +        if(err) goto exit;
 +        err = Parser_pop(p);
 +    } else if(c == 'x'){
 +        p->state->fn = state_hex;
 +        p->state->ival = 0;
 +        p->state->count = 0;
 +    } else {
 +        p->state->fn = state_octal;
 +        p->state->ival = 0;
 +        p->state->count = 0;
 +        err = Parser_input_char(p, c);
 +    }
 +  exit:
 +    return err;
 +}
 +
 +int octaldone(Parser *p){
 +    int err = 0;
 +    char d = (char)(p->state->ival & 0xff);
 +    err = Parser_pop(p);
 +    if(err) goto exit;
 +    err = Parser_input_char(p, d);
 +  exit:
 +    return err;
 +}
 +
 +int octaldigit(Parser *p, char c){
 +    int err = 0;
 +    p->state->ival *= 8;
 +    p->state->ival += c - '0'; 
 +    p->state->count++;
 +    if(err) goto exit;
 +    if(p->state->ival < 0 || p->state->ival > 0xff){
 +        parse_error(p);
 +        err = -EINVAL;
 +        goto exit;
 +    }
 +    if(p->state->count == 3){
 +        err = octaldone(p);
 +    }
 +  exit:
 +    return err;
 +}
 +
 +int state_octal(Parser *p, char c){
 +    int err = 0;
 +    if(at_eof(p)){
 +        parse_error_id(p, PARSE_ERR_UNEXPECTED_EOF);
 +        err = -EINVAL;
 +        goto exit;
 +    } else if('0' <= c && c <= '7'){
 +        err = octaldigit(p, c);
 +    } else {
 +        err = octaldone(p);
 +        if(err) goto exit;
 +        Parser_input_char(p, c);
 +    }
 +  exit:
 +    return err;
 +}
 +
 +int hexdone(Parser *p){
 +    int err = 0;
 +    char d = (char)(p->state->ival & 0xff);
 +    err = Parser_pop(p);
 +    if(err) goto exit;
 +    err = Parser_input_char(p, d);
 +  exit:
 +    return err;
 +}
 +    
 +int hexdigit(Parser *p, char c, char d){
 +    int err = 0;
 +    p->state->ival *= 16;
 +    p->state->ival += c - d; 
 +    p->state->count++;
 +    if(err) goto exit;
 +    if(p->state->ival < 0 || p->state->ival > 0xff){
 +        parse_error(p);
 +        err = -EINVAL;
 +        goto exit;
 +    }
 +    if(p->state->count == 2){
 +        err = hexdone(p);
 +    }
 +  exit:
 +    return err;
 +}
 +    
 +int state_hex(Parser *p, char c){
 +    int err = 0;
 +    if(at_eof(p)){
 +        parse_error_id(p, PARSE_ERR_UNEXPECTED_EOF);
 +        err = -EINVAL;
 +        goto exit;
 +    } else if('0' <= c && c <= '9'){
 +        err = hexdigit(p, c, '0');
 +    } else if('A' <= c && c <= 'F'){
 +        err = hexdigit(p, c, 'A');
 +    } else if('a' <= c && c <= 'f'){
 +        err = hexdigit(p, c, 'a');
 +    } else if(p->state->count){
 +        err =hexdone(p);
 +        if(err) goto exit;
 +        Parser_input_char(p, c);
 +    }
 +  exit:
 +    return err;
 +}
 +
 +int begin_atom(Parser *p, char c){
 +    int err = 0;
 +    err = Parser_push(p, state_atom, "atom");
 +    if(err) goto exit;
 +    newtoken(p);
 +    err = savechar(p, c);
 +  exit:
 +    return err;
 +}
 +
 +int state_atom(Parser *p, char c){
 +    int err = 0;
 +    if(at_eof(p)){
 +        err = end_atom(p);
 +    } else if(is_separator(p, c) ||
 +              in_space_class(c) ||
 +              in_comment_class(c)){
 +        err = end_atom(p);
 +        if(err) goto exit;
 +        err = Parser_input_char(p, c);
 +    } else {
 +        err = savechar(p, c);
 +    }
 +  exit:
 +    return err;
 +}
 +
 +int end_atom(Parser *p){
 +    int err = 0;
 +    err = do_intern(p);
 +    if(err) goto exit;
 +    err = Parser_return(p);
 +  exit:
 +    return err;
 +}
 +
 +int state_list(Parser *p, char c){
 +    int err = 0;
 +    if(at_eof(p)){
 +        parse_error_id(p, PARSE_ERR_UNEXPECTED_EOF);
 +        err = -EINVAL;
 +    } else if(c == c_list_close){
 +        p->state->val = nrev(p->state->val);
 +        err = end_list(p);
 +    } else {
 +        err = state_start(p, c);
 +    }
 +    return err;
 +    
 +}
 +
 +int begin_list(Parser *p, char c){
 +    return Parser_push(p, state_list, "list");
 +}
 +
 +int end_list(Parser *p){
 +    return Parser_return(p);
 +}
 +
 +/** Reset the fields of a parser to initial values.
 + *
 + * @param z parser
 + */
 +static void reset(Parser *z){
 +  IOStream *error_out = z->error_out;
 +  int flags = z->flags;
 +  memzero(z, sizeof(Parser));
 +  z->buf_n = sizeof(z->buf) - 1;
 +  z->buf_i = 0;
 +  z->line_no = 1;
 +  z->char_no = 0;
 +  z->error_out = error_out;
 +  z->flags = flags;
 +}
 +
 +/** Set the parser error stream.
 + * Parse errors are reported on the the error stream if it is non-null.
 + * 
 + * @param z parser
 + * @param error_out error stream
 + */
 +void set_error_stream(Parser *z, IOStream *error_out){
 +  if(z){
 +    z->error_out = error_out;
 +  }
 +}
 +
 +/** Get the parser error message for an error code.
 + *
 + * @param id error code
 + * @return error message (empty string if the code is unknown)
 + */
 +static char *get_message(ParseErrorId id){
 +  int i;
 +  for(i=0; i<catalog_n; i++){
 +    if(id == catalog[i].id){
 +      return catalog[i].message;
 +    }
 +  }
 +  return "";
 +}
 +
 +/** Get the line number.
 + *
 + * @param in parser
 + */
 +int get_line(Parser *in){
 +  return in->line_no;
 +}
 +
 +/** Get the column number.
 + *
 + * @param in parser
 + */
 +int get_column(Parser *in){
 +  return in->char_no;
 +}
 +
 +/** Get the line number the current token started on.
 + *
 + * @param in parser
 + */
 +int get_tok_line(Parser *in){
 +  return in->tok_begin_line;
 +}
 +
 +/** Get the column number the current token started on.
 + *
 + * @param in parser
 + */
 +int get_tok_column(Parser *in){
 +  return in->tok_begin_char;
 +}
 +
 +/** Report a parse error.
 + * Does nothing if the error stream is null or there is no error.
 + *
 + * @param in parser
 + */
 +static void report_error(Parser *in){
 +  if(in->error_out && in->err){
 +    char *msg = get_message(in->err);
 +    char *tok = peek_token(in);
 +    IOStream_print(in->error_out, PARSE_ERR_FMT,
 +                 get_tok_line(in), get_tok_column(in), msg);
 +    if(tok && tok[0]){
 +        IOStream_print(in->error_out, " '%s'", tok);
 +    }
 +    IOStream_print(in->error_out, "\n");
 +  }
 +}
 +
 +/** Get the error message for the current parse error code.
 + * Does nothing if there is no error.
 + *
 + * @param in parser
 + * @param buf where to place the message
 + * @param n maximum number of characters to place in buf
 + * @return current error code (zero for no error)
 + */
 +int parse_error_message(Parser *in, char *buf, int n){
 +    if(in->err){
 +        char *msg = get_message(in->err);
 +        snprintf(buf, n, PARSE_ERR_FMT, get_tok_line(in), get_tok_column(in), msg);
 +    }
 +    return in->err;
 +}
 +
 +/** Flag an unspecified parse error. All subsequent reads will fail.
 + *
 + * @param in parser
 + */
 +void parse_error(Parser *in){
 +    parse_error_id(in, PARSE_ERR_INVALID_SYNTAX);
 +}
 +
 +/** Flag a parse error. All subsequent reads will fail.
 + * Does not change the parser error code if it is already set.
 + *
 + * @param in parser
 + * @param id error code
 + */
 +void parse_error_id(Parser *in, ParseErrorId id){
 +    if(!in->err){
 +        in->err = id;
 +        report_error(in);
 +    }
 +}
 +
 +/** Test if the parser's error flag is set.
 + *
 + * @param in parser
 + * @return 1 if set, 0 otherwise
 + */
 +int has_error(Parser *in){
 +    return (in->err > 0);
 +}
 +
 +/** Test if the parser is at end of input.
 + *
 + * @param in parser
 + * @return 1 if at EOF, 0 otherwise
 + */
 +int at_eof(Parser *p){
 +    return p->eof;
 +}
 +
- int stringof(Sxpr exp, char **s){
-     int err = 0;
-     if(ATOMP(exp)){
-         *s = atom_name(exp);
-     } else if(STRINGP(exp)){
-         *s = string_string(exp);
-     } else {
-         err = -EINVAL;
-         *s = NULL;
-     }
-     return err;
- }
 +#ifdef SXPR_PARSER_MAIN
 +/* Stuff for standalone testing. */
 +
 +#include "file_stream.h"
 +#include "string_stream.h"
 +
- int intof(Sxpr exp, int *v){
-     int err = 0;
-     char *s;
-     unsigned long l;
-     if(INTP(exp)){
-         *v = OBJ_INT(exp);
-     } else {
-         err = stringof(exp, &s);
-         if(err) goto exit;
-         err = convert_atoul(s, &l);
-         *v = (int)l;
-     }
-  exit:
-     return err;
- }
++extern int stringof(Sxpr exp, char **s);
 +int child_string(Sxpr exp, Sxpr key, char **s){
 +    int err = 0;
 +    Sxpr val = sxpr_child_value(exp, key, ONONE);
 +    err = stringof(val, s);
 +    return err;
 +}
 +
++extern int intof(Sxpr exp, int *v);
 +int child_int(Sxpr exp, Sxpr key, int *v){
 +    int err = 0;
 +    Sxpr val = sxpr_child_value(exp, key, ONONE);
 +    err = intof(val, v);
 +    return err;
 +}
 +
 +int eval_vnet(Sxpr exp){
 +    int err = 0;
 +    Sxpr oid = intern("id");
 +    int id;
 +    err = child_int(exp, oid, &id);
 +    if(err) goto exit;
 +    dprintf("> vnet id=%d\n", id);
 + exit:
 +    dprintf("< err=%d\n", err);
 +    return err;
 +}
 +
 +int eval_connect(Sxpr exp){
 +    int err = 0;
 +    Sxpr ovif = intern("vif");
 +    Sxpr ovnet = intern("vnet");
 +    char *vif;
 +    int vnet;
 +
 +    err = child_string(exp, ovif, &vif);
 +    if(err) goto exit;
 +    err = child_int(exp, ovnet, &vnet);
 +    if(err) goto exit;
 +    dprintf("> connect vif=%s vnet=%d\n", vif, vnet);
 + exit:
 +    dprintf("< err=%d\n", err);
 +    return err;
 +}
 +
 +int eval(Sxpr exp){
 +    int err = 0;
 +    Sxpr oconnect = intern("connect");
 +    Sxpr ovnet = intern("vnet");
 +    
 +    if(sxpr_elementp(exp, ovnet)){
 +        err = eval_vnet(exp);
 +    } else if(sxpr_elementp(exp, oconnect)){
 +        err = eval_connect(exp);
 +    } else {
 +        err = -EINVAL;
 +    }
 +    return err;
 +}
 +
 +/** Main program for testing.
 + * Parses input and prints it.
 + *
 + * @param argc number of arguments
 + * @param argv arguments
 + * @return error code
 + */
 +int main(int argc, char *argv[]){
 +    Parser *pin;
 +    int err = 0;
 +    char buf[1024];
 +    int k;
 +    Sxpr obj;
 +    //Sxpr l, x;
 +    int i = 0;
 +
 +    pin = Parser_new();
 +    set_error_stream(pin, iostdout);
 +    dprintf("> parse...\n");
 +    while(1){
 +        k = fread(buf, 1, 1, stdin);
 +        err = Parser_input(pin, buf, k);
 +        while(Parser_ready(pin)){
 +            obj = Parser_get_val(pin);
 +            printf("obj %d\n", i++);
 +            objprint(iostdout, obj, 0); printf("\n");
 +        }
 +        if(k <= 0) break;
 +    }
 +/*     obj = Parser_get_all(pin); */
 +/*     for(l = obj ; CONSP(l); l = CDR(l)){ */
 +/*         x = CAR(l); */
 +/*         objprint(iostdout, x, 0); printf("\n"); */
 +/*         eval(x); */
 +/*     } */
 +    dprintf("> err=%d\n", err);
 +    return 0;
 +}
 +#endif